package cool.taomu.box.git

import java.io.File
import java.util.HashMap
import javax.inject.Inject
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.MergeCommand
import org.eclipse.jgit.api.ResetCommand
import org.eclipse.jgit.api.TransportCommand
import org.eclipse.jgit.api.TransportConfigCallback
import org.eclipse.jgit.lib.ObjectId
import org.eclipse.jgit.revwalk.RevWalk
import org.eclipse.jgit.transport.CredentialsProvider
import org.eclipse.jgit.transport.RefSpec

class GitUtils {
	@Inject
	TransportConfigCallback ssh

	@Inject
	CredentialsProvider cp;

	val String repository = "./repository";

	private def void credentials(TransportCommand<?, ?> command) {
		if (ssh !== null) {
			command.transportConfigCallback = ssh;
		} else if (cp !== null) {
			command.credentialsProvider = cp;
		}
	}

	private def getRepository(String remoteRepository) {
		var projectName = remoteRepository;
		if (remoteRepository.contains("/")) {
			projectName = remoteRepository.substring(remoteRepository.lastIndexOf("/") + 1)
		}
		var file = new File(#[repository, projectName].join(File.separator));
		if (!file.exists) {
			file.mkdirs;
		}
		return file;
	}

	def clone(String remoteRepository, boolean isAllBranches, String branchOrTag) {
		var file = getRepository(remoteRepository);
		file.mkdirs;
		var clone = Git.cloneRepository().setURI(remoteRepository).setBare(false)
		if (isAllBranches) {
			clone.setCloneAllBranches = isAllBranches;
		} else {
			clone.cloneAllBranches = isAllBranches;
			clone.branch = branchOrTag;
		}
		clone.setDirectory(file);
		this.credentials(clone);
		try (var git = clone.call()) {
			var refs = git.lsRemote.setHeads(true).setRemote(remoteRepository).call();
			var StringBuffer s = new StringBuffer();
			for (ref : refs) {
				s.append(ref.getName());
				s.append("\n");
			}
			s.append("clone success")
			return s.toString();
		}

	}

	def push(String remoteRepository, String name, String branch) {
		var file = getRepository(remoteRepository);
		var git = Git.open(file);
		var push = git.push.setRefSpecs(new RefSpec(branch)).setRemote(name);
		this.credentials(push);
		var infos = push.call()
		var buffer = new StringBuffer();
		for (info : infos) {
			buffer.append(info.messages);
			buffer.append("\n")
		}
		return buffer.toString();
	}

	def fetch(String remoteRepository) {
		var file = getRepository(remoteRepository);
		var refs = Git.lsRemoteRepository().setHeads(true).setRemote(remoteRepository).call();
		var log = new StringBuffer("fetch:\n");
		for (ref : refs) {
			var String refStr = ref.getName();
			var fetchCommand = Git.open(file).fetch().setRemote(remoteRepository).setRefSpecs(
				#[new RefSpec(refStr + ":" + refStr)])
			this.credentials(fetchCommand);
			var result = fetchCommand.call();
			log.append(result.getMessages());
			log.append("\n");
		}
		log.append("Fetch success")
		return log.toString();
	}

	def commit(String remoteRepository, String[] files, String msg) {
		var file = getRepository(remoteRepository);
		val git = Git.open(file);
		files.forEach [
			git.add().addFilepattern(it).call();
		]
		git.commit.setMessage(msg).call();
	}

	def status(String remoteRepository) {
		var map = new HashMap<String, String>();
		var file = getRepository(remoteRepository);
		var git = Git.open(file);
		var status = git.status.call();
		map.put("Added", status.added.toString());
		map.put("Changed", status.changed.toString());
		map.put("Conflicting", status.conflicting.toString());
		map.put("ConflictingStageState", status.conflictingStageState.toString());
		map.put("IgnoredNotInIndex", status.ignoredNotInIndex.toString());
		map.put("Missing", status.missing.toString());
		map.put("Modified", status.modified.toString());
		map.put("Removed", status.removed.toString());
		map.put("UntrackedFiles", status.untracked.toString());
		map.put("UntrackedFolders", status.untrackedFolders.toString());
		return map;
	}

	def branchCreate(String remoteRepository, String name) {
		var file = getRepository(remoteRepository);
		var git = Git.open(file);
		git.branchCreate.setName(name).call();
	}

	def branchDelete(String remoteRepository, String[] names) {
		var file = getRepository(remoteRepository);
		var git = Git.open(file);
		git.branchDelete.setBranchNames(names).call();
	}

	def branchRename(String remoteRepository, String oldName, String newName) {
		var file = getRepository(remoteRepository);
		var git = Git.open(file);
		git.branchRename.setNewName(newName).setOldName(oldName).call;
	}

	def branchList(String remoteRepository) {
		var file = getRepository(remoteRepository);
		var git = Git.open(file);
		val result = newArrayList;
		git.branchList.call.forEach [
			result.add(it.name);
		]
		return result;
	}

	def checkout(String remoteRepository, String name) {
		var file = getRepository(remoteRepository);
		var git = Git.open(file);
		var ck = git.checkout.name = name;
		ck.call;
	}

	def merge(String remoteRepository, String srcName, String mergeBranch, String mgs) {
		var file = getRepository(remoteRepository);
		var git = Git.open(file);
		var ref = git.checkout.setName(srcName).call();
		git.checkout.setName(mergeBranch).call();
		var merge = git.merge.include(ref).setCommit(true).fastForward = MergeCommand.FastForwardMode.NO_FF
		merge.message = mgs;
		merge.call;
	}

	def pull(String remoteRepository, String remoteBranch) {
		var file = getRepository(remoteRepository);
		val git = Git.open(file);
		var pull = git.pull.remoteBranchName = remoteBranch;
		this.credentials(pull);
		pull.call;
	}

	def reset(String remoteRepository, String objectId) {
		var file = getRepository(remoteRepository);
		val git = Git.open(file);
		var walk = new RevWalk(git.repository);
		var objId = git.repository.resolve(objectId);
		var revCommit = walk.parseCommit(objId);
		var vision = revCommit.getParent(0).getName();
		git.reset.setMode(ResetCommand.ResetType.HARD).setRef(vision).call;
	}

	def revert(String remoteRepository, String commitId) {
		var file = getRepository(remoteRepository);
		val git = Git.open(file);
		var walk = new RevWalk(git.repository);
		var revCommit = walk.parseCommit(ObjectId.fromString(commitId));
		git.revert.include(revCommit).call();
	}

}
